package org.hit.burkun.swalk.lap.norm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

import org.hit.burkun.swalk.FeatureField;
import org.hit.burkun.swalk.RandomWalkGraph;

import cern.colt.matrix.tdouble.DoubleMatrix1D;

class IntEdge {
	public int uid;
	public double score;

	public IntEdge(int uid, double score) {
		this.uid = uid;
		this.score = score;
	}
}

public class LinkedListNetworkWithLaplac extends RandomWalkGraph {

	protected double[] rowSums;
	protected ArrayList<HashMap<Integer, Double>> adjLinks;
	protected ArrayList<HashMap<Integer, Double>> QAdjLinks;
	protected ArrayList<HashMap<Integer, Double>> dQAdjLinks;
	protected ArrayList<Double> laplac;
	protected boolean useWeiFunc = true;
    protected  ArrayList<ArrayList<HashMap<Integer, Double>>> dAdjLinksWithWeight ;
	
	public LinkedListNetworkWithLaplac(){}
	public LinkedListNetworkWithLaplac(int n, int s, int f, ArrayList<FeatureField> list,
			ArrayList<Integer> D, ArrayList<Integer> L, boolean useWeiFunc) {
		super(n, s, f, list, D, L);
		rowSums = new double[n];
		this.useWeiFunc = useWeiFunc;
	}
	
	private void initLap(){
		laplac = new ArrayList<>(dim);
		dAdjLinksWithWeight = new ArrayList<>(f);
	}
	
	private void initAdj() {
		//init lap
		initLap();
		adjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			adjLinks.add(i, new HashMap<Integer, Double>());
		}
		//chushihua
		for(int i= 0; i<f; i++){
			dAdjLinksWithWeight.add(new ArrayList<HashMap<Integer, Double>>(dim));
			for (int j = 0; j < dim; j++) {
				dAdjLinksWithWeight.get(i).add(j, new HashMap<Integer, Double>());
			}
		}
	}
	
	private void initQadj() {
		QAdjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			QAdjLinks.add(i, new HashMap<Integer, Double>());
		}
	}

	private void initDqAdj() {
		dQAdjLinks = new ArrayList<>(dim);
		// init arr, 从0开始
		for (int i = 0; i < dim; i++) {
			dQAdjLinks.add(i, new HashMap<Integer, Double>());
		}
	}

	
	@Override
	public void buildAdjacencyMatrix(DoubleMatrix1D param) {
		initAdj();
		
		// super.buildAdjacencyMatrix(param);
		for (FeatureField ff : list) {
			int c = ff.column;
			int r = ff.row;
			// reverse
			DoubleMatrix1D df = ff.features;
			double score = df.zDotProduct(param);
			double dScore = 0;
			if(useWeiFunc){
				for(int i=0; i< f; i++){
					dScore = weightingFunctionDerivative(score, i);
					dAdjLinksWithWeight.get(i).get(r).put(c, dScore);
					dAdjLinksWithWeight.get(i).get(c).put(r, dScore);
				}
				score = weightingFunction(score);
			}
			adjLinks.get(c).put(r, score);
			adjLinks.get(r).put(c, score);
		}
		//init lap
		for(int i=0; i<dim; i++){
			//计算周边的值sum(Aij)
			HashMap<Integer, Double> edges = adjLinks.get(i);
			Collection<Double> ss = edges.values();
			double sums = 0;
			for(Double v : ss){
				sums += v;
			}
			laplac.add(i, sums);
		}
		//norlaized
		for (FeatureField ff : list) {
			int c = ff.column;
			int r = ff.row;
			double sums = Math.sqrt(laplac.get(c) * laplac.get(r));
			double adjV = adjLinks.get(c).get(r) / sums;
			adjLinks.get(c).put(r, adjV);
			adjLinks.get(r).put(c, adjV);
		}
		
	}

	public ArrayList<HashMap<Integer, Double>> buildTransitionTranspose(
			double alpha) {
		// 改过来
		initQadj(); // 一定要这么做！
		// SparseCCDoubleMatrix2D Q = new SparseCCDoubleMatrix2D(
		// this.A.rows(), this.A.columns());
		// row sums
		int r, c;
		for (int i = 0; i < this.dim; rowSums[i++] = 0)
			;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			rowSums[r] += this.adjLinks.get(r).get(c);
			if (r != c)
				rowSums[c] += this.adjLinks.get(c).get(r);
		}

		// (1-alpha) * A[i][j] / sumElements(A[i])) + 1(j == s) * alpha
		// build the transpose of Q
		double value;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			value = this.adjLinks.get(r).get(c);
			value *= (1 - alpha);
			// value /= rowSums[r];
			// Q.set(c, r, value);
			QAdjLinks.get(c).put(r, value / rowSums[r]);

			if (r == c)
				continue;

			// value = this.A.get(c, r);
			// value *= (1 - alpha);
			// value /= rowSums[c];
			QAdjLinks.get(r).put(c, value / rowSums[c]);
		}

		for (int i = 0; i < QAdjLinks.size(); i++) {
			if (QAdjLinks.get(this.s).containsKey(i)) {
				value = QAdjLinks.get(this.s).get(i);
				value += alpha;
				QAdjLinks.get(this.s).put(i, value);
			} else {
				QAdjLinks.get(this.s).put(i, alpha);
			}
		}
		return QAdjLinks;
	}

	@Override
	public ArrayList<HashMap<Integer, Double>> transitionDerivativeTranspose(
			int featureIndex, double alpha, DoubleMatrix1D param) {
		initDqAdj();
		// SparseCCDoubleMatrix2D dQt = new SparseCCDoubleMatrix2D(this.dim,
		// this.dim);
		// System.out.println("transitionDerivativeTranspose...");
		// derivative row sums
		int r, c;
		double[] dRowSums = new double[this.dim];
		ArrayList<Double> cacheDeri = new ArrayList<>(list.size());
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			double x = this.list.get(i).features.zDotProduct(param);
			double fs = list.get(i).features.get(featureIndex);
			double dw = getLaplacFunctionDeriVative(r, c, x, fs, featureIndex);
			cacheDeri.add(i, dw);
			dRowSums[r] += dw;
			if (r != c)
				dRowSums[c] += dw;
		}
		System.out.println("sum done!");
		double value;
		for (int i = 0; i < this.list.size(); i++) {
			r = this.list.get(i).row;
			c = this.list.get(i).column;
			double score = 0;
			if (adjLinks.get(r).containsKey(c)) {
				score = adjLinks.get(r).get(c);
			}
			//double dw = getLaplacFunctionDeriVative(r, c, x, fs, featureIndex);
			double dw = cacheDeri.get(i);
			value = (dw * rowSums[r]) - (score * dRowSums[r]);
			value *= (1 - alpha);
			value /= Math.pow(rowSums[r], 2);
			dQAdjLinks.get(c).put(r, value);
			// dQt.set(c, r, value);

			if (c == r)
				continue;

			double score1 = 0;
			if (adjLinks.get(c).containsKey(r)) {
				score1 = adjLinks.get(c).get(r);
			}
			value = (dw * rowSums[c]) - (score1 * dRowSums[c]);
			value *= (1 - alpha);
			value /= Math.pow(rowSums[c], 2);
			// dQt.set(r, c, value);
			dQAdjLinks.get(r).put(c, value);
		}
		return dQAdjLinks;
	}

	@Override
	public double weightingFunction(double x) {
		// return Math.exp(x);
		if (x > 30)
			return 1.0;
		if (x < -30)
			return 0.0;
		return 1.0 / (1.0 + Math.exp(-x));
	}

	@Override
	public double weightingFunctionDerivative(double x, double featureValue) {
		if (x > -100 && x < 100) {
			double ex = Math.exp(x);
			return ex / ((1.0 + ex) * (1.0 + ex)) * (featureValue);
		} else {
			return 0.0;
		}
		// HashMap<Integer, Double> edges = adjLinks.get(row);
		// if(edges.containsKey(column)){
		// return edges.get(column) * edges.get(column) *
		// Math.exp(-featureValue);
		// }
		// return 0;
	}

	@Override
	public boolean hasLink(int from, int to) {
		HashMap<Integer, Double> edges = adjLinks.get(from);
		return edges.containsKey(to);
	}

	@Override
	public void pageRankAndGradient(DoubleMatrix1D param, double alpha,
			double eps) {
		//计算
		super.pageRankAndGradient(param, alpha, eps);
	}

	
	//laplac
	/*
	 * r row
	 * c cow
	 * x value
	 * featureValue weight
	 * d(Aij/(sqrt(sum(Aij)*sum(Aij))
	 */
	private double getLaplacFunctionDeriVative(int r, int c, double x, double featureValue, int featureIndex){
		double fenMu = laplac.get(c) * laplac.get(r);
		double dArc = weightingFunctionDerivative(x, featureValue);
		double sqrtSum = Math.sqrt(fenMu);
		double arc = adjLinks.get(r).get(c) * Math.sqrt(fenMu); //restore init value
		double dSqrtSum = 0;
		HashMap<Integer, Double> edgeR = adjLinks.get(r);
		HashMap<Integer, Double> edgeC = adjLinks.get(c);
		Set<Integer> keyR = edgeR.keySet();
		Set<Integer> keyC = edgeC.keySet();
		//对（Sum(Aju)*Sum(Avi)求导
		for(int ridx : keyR){
			double ridxDArc = dAdjLinksWithWeight.get(featureIndex).get(r).get(ridx);
			double ridxArc = adjLinks.get(r).get(ridx) * Math.sqrt(laplac.get(r) * laplac.get(ridx));
			for(int cidx : keyC){
				double cidxDArc = dAdjLinksWithWeight.get(featureIndex).get(c).get(cidx);
				double cidxArc = adjLinks.get(c).get(cidx) * Math.sqrt(laplac.get(c) * laplac.get(cidx));
				dSqrtSum += ridxArc * cidxDArc + ridxDArc * cidxArc;
			}
		}
		dSqrtSum = dSqrtSum / (2*(sqrtSum));
		return (dArc * sqrtSum - dSqrtSum * arc)/fenMu;
	}
}
